﻿
// **************************************************************************
//    Class Created by Mick Doherty (Dotnetrix) March 2010
//    http://dotnetrix.co.uk/
//
//    ...for GlassUI (an AeroGlass UI Library)
//    http://glassui.codeplex.com/
//    
//    ...modified by element109 July 2010
// **************************************************************************

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Security.Permissions;
using System.Windows.Forms;
using GlassUI.Design;

namespace GlassUI
{
    [Designer(typeof(Design.AutoSizeControlDesigner))]
    [ToolboxBitmap(typeof(Button))]
    public class GlassButton : GlassAutoSizeControlBase, IButtonControl
    {
        public GlassButton()
            : base()
        {
            this.SetStyle(ControlStyles.Selectable | ControlStyles.StandardClick, true);
        }

        #region Private Instance Variables

        private DialogResult dialogResult;
        private bool isDefault;

        private ControlState buttonState = ControlState.Normal;

        private ContentAlignment imageAlign = ContentAlignment.MiddleCenter;
        private ContentAlignment textAlign = ContentAlignment.MiddleCenter;
        private TextImageRelation textImageRelation = TextImageRelation.Overlay;

        private ImageList imageList;
        private int imageIndex = -1;
        private string imageKey = "";

        private Image image;

        private bool keyPressed;

        #endregion

        #region IButtonControl Implementation

        [Category("Behavior"), DefaultValue(typeof(DialogResult), "None")]
        [GlassUIDescription("ButtonDialogResultDescr", "System.Windows.Forms")]
        public DialogResult DialogResult
        {
            get { return dialogResult; }
            set
            {
                if (Enum.IsDefined(typeof(DialogResult), value))
                    dialogResult = value;
            }
        }

        public void NotifyDefault(bool value)
        {
            if (isDefault != value)
                isDefault = value;
            this.Invalidate();
        }

        public void PerformClick()
        {
            if (this.CanSelect)
                base.OnClick(EventArgs.Empty);
        }

        #endregion

        #region Properties

        [Browsable(false)]
        public ControlState ButtonState
        {
            get { return buttonState; }
        }

        protected override System.Drawing.Size DefaultSize
        {
            get { return new Size(75, 23); }
        }

        [Browsable(false)]
        public bool IsDefault
        {
            get { return isDefault; }
        }

        [GlassUIDescription("ButtonImageDescr", "System.Windows.Forms")]
        public Image Image
        {
            get { return image; }
            set
            {
                if (value != image)
                {
                    image = value;
                    if (value != null)
                        this.ImageList = null;
                    if (this.AutoSize)
                        this.OnSizeChanged(EventArgs.Empty);
                    this.Invalidate();
                }
            }
        }

        [Category("Appearance"), DefaultValue(typeof(ImageList), null)]
        [GlassUIDescription("ButtonImageListDescr", "System.Windows.Forms")]
        public ImageList ImageList
        {
            get { return imageList; }
            set
            {
                imageList = value;
                if (value != null)
                    this.Image = null;
                this.Invalidate();
            }
        }

        [Category("Appearance"), DefaultValue(-1)]
        [Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design", typeof(UITypeEditor))]
        [GlassUIDescription("ButtonImageIndexDescr", "System.Windows.Forms")]
        [TypeConverter(typeof(ImageIndexConverter))]
        public int ImageIndex
        {
            get
            {
                if (this.imageList == null)
                    return -1;
                if (this.imageList.Images.Count < imageIndex)
                    return -1;
                return imageIndex;
            }
            set
            {
                imageIndex = value;
                if (imageKey.Length > 0)
                    imageKey = "";
                if (this.AutoSize)
                    this.OnSizeChanged(EventArgs.Empty);
                this.Invalidate();
            }
        }

        [DefaultValue("")]
        [Editor("System.Windows.Forms.Design.ImageIndexEditor, System.Design", typeof(UITypeEditor))]
        [GlassUIDescription("ButtonImageIndexDescr", "System.Windows.Forms")]
        [TypeConverter(typeof(ImageKeyConverter))]
        [RefreshProperties(RefreshProperties.All)]
        public string ImageKey
        {
            get
            {
                if (this.imageList == null)
                    return "";
                if (!this.imageList.Images.ContainsKey(imageKey))
                    return "";
                return imageKey;
            }
            set
            {
                imageKey = value;
                if (imageIndex != -1)
                    imageIndex = -1;
                if (this.AutoSize)
                    this.OnSizeChanged(EventArgs.Empty);
                this.Invalidate();
            }
        }

        [Category("Appearance"), DefaultValue(typeof(ContentAlignment), "MiddleCenter")]
        [GlassUIDescription("ButtonImageAlignDescr", "System.Windows.Forms")]
        public ContentAlignment ImageAlign
        {
            get { return imageAlign; }
            set
            {
                if (!Enum.IsDefined(typeof(ContentAlignment), value))
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment));
                if (imageAlign == value)
                    return;
                imageAlign = value;
                this.Invalidate();
            }
        }

        [Category("Appearance"), DefaultValue(typeof(ContentAlignment), "MiddleCenter")]
        [GlassUIDescription("ButtonTextAlignDescr", "System.Windows.Forms")]
        public ContentAlignment TextAlign
        {
            get { return textAlign; }
            set
            {
                if (!Enum.IsDefined(typeof(ContentAlignment), value))
                    throw new InvalidEnumArgumentException("value", (int)value, typeof(ContentAlignment));
                if (textAlign == value)
                    return;
                textAlign = value;
                this.Invalidate();
            }
        }

        [Category("Behavior"), DefaultValue(typeof(TextImageRelation), "Overlay")]
        [GlassUIDescription("ButtonTextImageRelationDescr", "System.Windows.Forms")]
        public TextImageRelation TextImageRelation
        {
            get{ return textImageRelation; }
            set
            {
                if (textImageRelation != value)
                {
                    if (Enum.IsDefined(typeof(TextImageRelation), value))
                    {
                        textImageRelation = value;
                        this.Invalidate();
                    }
                    else
                    {
                        throw new InvalidEnumArgumentException("value", (int)value, typeof(TextImageRelation));
                    }
                }
            }
        }

        #endregion

        #region Overriden Methods

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (e.KeyCode == Keys.Space)
            {
                keyPressed = true;
                buttonState = ControlState.Pressed;
            }
            OnStateChange(EventArgs.Empty);
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);
            if (e.KeyCode == Keys.Space)
            {
                if (this.ButtonState == ControlState.Pressed)
                    this.PerformClick();
                keyPressed = false;
                buttonState = ControlState.Focused;
            }
            OnStateChange(EventArgs.Empty);
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            if (GetHitTest(this.PointToClient(Cursor.Position)))
            {
                if (!keyPressed)
                    buttonState = ControlState.Hot;
                OnStateChange(EventArgs.Empty);
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            if (!keyPressed)
                if (this.IsDefault)
                    buttonState = ControlState.Focused;
                else
                    buttonState = ControlState.Normal;
            OnStateChange(EventArgs.Empty);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (GetHitTest(this.PointToClient(Cursor.Position)))
            {

                if (e.Button == MouseButtons.Left)
                {
                    this.Focus();
                    buttonState = ControlState.Pressed;
                }
                OnStateChange(EventArgs.Empty);
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (GetHitTest(this.PointToClient(Cursor.Position)))
            {

                buttonState = ControlState.Focused;
                OnStateChange(EventArgs.Empty);
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (GetHitTest(this.PointToClient(Cursor.Position)))
            {

                if (new Rectangle(Point.Empty, this.Size).Contains(e.X, e.Y) && e.Button == MouseButtons.Left)
                    buttonState = ControlState.Pressed;
                else
                {
                    if (keyPressed)
                        return;
                    buttonState = ControlState.Hot;
                }
            }
            else
            {
                buttonState = ControlState.Normal;
            }
            OnStateChange(EventArgs.Empty);
        }

        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            buttonState = ControlState.Focused;
            this.NotifyDefault(true);
        }

        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            if (this.FindForm().Focused)
                this.NotifyDefault(false);
            buttonState = ControlState.Normal;
        }

        protected override void OnEnabledChanged(EventArgs e)
        {
            base.OnEnabledChanged(e);
            if (this.Enabled)
                buttonState = ControlState.Normal;
            else
                buttonState = ControlState.Disabled;
            OnStateChange(EventArgs.Empty);
        }

        protected override void OnClick(EventArgs e)
        {
            //Click gets fired before MouseUp which is handy
            if (GetHitTest(this.PointToClient(Cursor.Position)))
            {

                if (this.ButtonState == ControlState.Pressed)
                {
                    this.Focus();
                    this.PerformClick();
                }
            }
        }

        protected override void OnDoubleClick(EventArgs e)
        {
            if (GetHitTest(this.PointToClient(Cursor.Position)))
            {

                if (this.ButtonState == ControlState.Pressed)
                {
                    this.Focus();
                    this.PerformClick();
                }
            }
        }

        [UIPermissionAttribute(SecurityAction.InheritanceDemand, Window = UIPermissionWindow.AllWindows)]
        [UIPermissionAttribute(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)]
        protected override bool ProcessMnemonic(char charCode)
        {
            if (IsMnemonic(charCode, this.Text))
            {
                base.OnClick(EventArgs.Empty);
                return true;
            }
            return base.ProcessMnemonic(charCode);
        }

        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            this.Invalidate();
        }

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            base.OnPaintBackground(pevent);

            // Set up smooth drawing
            pevent.Graphics.SmoothingMode = SmoothingMode.HighQuality;
            pevent.Graphics.CompositingQuality = CompositingQuality.HighQuality;

            base.DrawParentBackground(pevent.Graphics);

            using (GraphicsPath path = GlassUtilities.RoundedRectangle(this.ClientRectangle, this.CornerRadius, this.RoundedCorners))
            {
                pevent.Graphics.SetClip(path, CombineMode.Replace);
                GlassUtilities.DrawGlowGradient(pevent.Graphics, this.ClientRectangle, this.BackColor, this.GlowColor, this.ButtonState);
                pevent.Graphics.ResetClip();
                bool drawDefault = this.ButtonState == ControlState.Pressed || this.IsDefault;
                GlassUtilities.DrawRoundedBorder(pevent.Graphics, this.ClientRectangle, this.CornerRadius, this.RoundedCorners, drawDefault ? this.GlowColor : Color.Empty);
            }

        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Rectangle rc = this.ClientRectangle;
            rc.Inflate(-2, -2);
            Rectangle contentRect = Rectangle.FromLTRB(Margin.Left, Margin.Top, Width - Margin.Right, Height - Margin.Bottom); //contentRect;
            
            Image img = this.Image;
            if (this.ImageList != null)
            {
                if (this.imageIndex != -1)
                    img = this.ImageList.Images[this.imageIndex];
                if (this.imageKey.Length > 0)
                    img = this.imageList.Images[this.imageKey];
            }

            using (GraphicsPath path = GlassUtilities.RoundedRectangle(rc, this.CornerRadius, this.RoundedCorners))
            {
                e.Graphics.SetClip(path, CombineMode.Replace);
                Color textColor = this.Enabled ? this.ForeColor : SystemColors.GrayText;
                GlassUtilities.RenderStandardContent(e.Graphics, contentRect, this.Text, this.Font, img, this.TextAlign, this.ImageAlign, this.TextImageRelation, textColor, true, 8, this.Enabled);
                e.Graphics.ResetClip();
            }

            if (this.Focused && this.ShowFocusCues && this.TabStop)
                ControlPaint.DrawFocusRectangle(e.Graphics, rc, this.ForeColor, this.BackColor);

        }

        protected override void OnParentBackColorChanged(EventArgs e)
        {
            base.OnParentBackColorChanged(e);
            this.Invalidate();
        }

        protected override void OnParentBackgroundImageChanged(EventArgs e)
        {
            base.OnParentBackgroundImageChanged(e);
            this.Invalidate();
        }

        #endregion

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            if (this.AutoSize)
                this.OnAutoSizeChanged();
        }

        public override Size GetPreferredSize(Size proposedSize)
        {
            // return base.GetPreferredSize(proposedSize);
            Size imageSize = Size.Empty;
            Size availableTextSize = Size.Empty;
            Size textSize = Size.Empty;

            Rectangle contentBounds = Rectangle.Empty;
            TextFormatFlags format = TextFormatFlags.TextBoxControl | TextFormatFlags.WordBreak;

            Image img = this.Image;
            if (this.ImageList != null)
            {
                if (this.imageIndex != -1)
                    img = this.ImageList.Images[this.imageIndex];
                if (this.imageKey.Length > 0)
                    img = this.imageList.Images[this.imageKey];
            }

            if (img != null)
                imageSize = img.Size;

            switch (this.TextImageRelation)
            {
                case TextImageRelation.ImageAboveText:
                case TextImageRelation.TextAboveImage:
                    availableTextSize = new Size(proposedSize.Width, proposedSize.Height - imageSize.Height);
                    break;

                case TextImageRelation.ImageBeforeText:
                case TextImageRelation.TextBeforeImage:
                    availableTextSize = new Size(proposedSize.Width - imageSize.Width, proposedSize.Height);
                    break;

                default:
                    availableTextSize = proposedSize;
                    break;
            }

            if (this.Text.Length > 0)
            {
                using (Graphics graphics = this.CreateGraphics())
                {
                    textSize = GlassUtilities.MeasureThemedText(graphics, this.Text, this.Font, availableTextSize, format, 8);
                }
            }

            switch (this.TextImageRelation)
            {
                case TextImageRelation.ImageAboveText:
                case TextImageRelation.TextAboveImage:
                    contentBounds.Width = Math.Max(imageSize.Width, textSize.Width);
                    contentBounds.Height = imageSize.Height + textSize.Height;
                    break;

                case TextImageRelation.ImageBeforeText:
                case TextImageRelation.TextBeforeImage:
                    contentBounds.Width = imageSize.Width + textSize.Width;
                    contentBounds.Height = Math.Max(imageSize.Height, textSize.Height);
                    break;

                default:
                    contentBounds.Width = Math.Max(imageSize.Width, textSize.Width);
                    contentBounds.Height = Math.Max(imageSize.Height, textSize.Height);
                    break;
            }

            contentBounds.Width += Padding.Horizontal + 8;
            contentBounds.Height += Padding.Vertical + 8 ;

            return contentBounds.Size;
        }

        private bool GetHitTest(Point point)
        {
            using (GraphicsPath path = GlassUtilities.RoundedRectangle(this.ClientRectangle, this.CornerRadius, this.RoundedCorners))
            {
                return path.IsVisible(point);
            }
        }

        private ControlState currentState;
        protected virtual void OnStateChange(EventArgs e)
        {
            //Repaint the button only if the state has actually changed
            if (this.ButtonState == currentState)
                return;
            currentState = this.ButtonState;
            this.Invalidate();
        }

    }

}
